﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using alib.Debugging;

using alib.Hashing;
using alib.Enumerable;
using alib.Collections;
using alib.Array;
using alib.dg;
#if DEBUG
using alib.dg.debug;
#endif

namespace agree
{
	//using vertex = System.Int32;
	//using edge = System.Int32;

	public unsafe class ztfs : ro_graph, IGraphData<FeatureInfo, agree.Type>, IVdata<agree.Edge.Flag>, IStringGraph
	{
		public ztfs(ztfs ztfs)
			: base(ztfs.E, ztfs.V)
		{
			this.im = ztfs.im;
		}
		public ztfs(Tfs tfs, agree.Edge e)
			: base(build_ztfs(tfs, e))
		{
			this.im = tfs.im;
#if DEBUG
			new check_helper(this);
#endif
		}
		public ztfs(ITfsSlot ts)
			: this(ts.Tfs, ts.Edge())
		{
		}

		public readonly Isometrics im;

		/// base string edge/vertex interpretations

		public override String edge_string(Eref er) { return im.ftm[E[er].value].FEATURE; }

		public override String vertex_string(Vref vr)
		{
			return im.tu.TypeNameOrStringValueDisplay((agree.Edge.Flag)V[vr].value);
		}

		/// native edge (FeatureInfo) interpretation

		public IIndexedHash<FeatureInfo> EDict { get { return im.ftm; } }

		public FeatureInfo EdgeData(Eref e) { return im.ftm[edge_value(e)]; }

		/// native vertex (Type; Edge.Flag) interpretations

		IIndexedHash<Type> IVindex<Type>.VDict { get { return im.bcpo; } }

		public agree.Edge.Flag VertexData(Vref v) { return (agree.Edge.Flag)vertex_value(v); }

		agree.Type IVdata<agree.Type>.VertexData(Vref v) { return im.tu.GetEdgeType(VertexData(v)); }

		/// string edge/vertex interpretations

		IIndexedHash<String> IEindex<String>.EDict { get { return im.ftm; } }

		IIndexedHash<String> IVindex<String>.VDict { get { return im.tu; } }

		String IEdata<String>.EdgeData(Eref er) { return edge_string(er); }

		String IVdata<String>.VertexData(Vref vr) { return vertex_string(vr); }

		/// don't report as lists derived types which have added features
		public bool vertex_is_list(Vref v)
		{
			int _head = im.f_ix_list_head, _tail = im.f_ix_list_tail, fix;
			var arr = vertex_out_values(v);
			for (int i = 0; i < arr.Length; i++)
			{
				if (i == 2)
					return false;
				if ((fix = arr[i]) == _head)
					_head = -1;
				else if (fix == _tail)
					_tail = -1;
			}
			return _head + _tail == -2;
		}
		public bool vertex_is_diff_list(Vref v)
		{
			int fix = im.f_ix_dlist_list;
			return fix != -1 && Array.IndexOf(vertex_out_values(v), fix) != -1;
		}
		public bool edge_target_diff_list(Eref e)
		{
			return edge_value(e) == im.f_ix_dlist_list;
		}

		public Eref[] vertex_out_edges_display_order(Vref v)
		{
			var fa = im.ftm.feat_arr;
			var oe = vertex_out_edges(v);
			int c;
			var sk = new int[c = oe.Length];
			for (int i = 0; i < c; i++)
				sk[i] = fa[E[oe[i]].value].OrderIndex;

			Array.Sort<int, Eref>(sk, oe);
			return oe;
		}

		//public IEnumerable<Vref> vertex_diff_list_nodes(Vref v)
		//{
		//	//yield break;
		//	//Edge e = GetIx1Edge(ix1);
		//	//if (e.Mark != 0 && (ix1 = GetEdgeIndex(im.f_ix_dlist_list, e.Mark)) > 0)
		//	foreach (var vv in vertex_list_nodes(v))
		//		yield return vv;
		//}

		public IEnumerable<Vref> vertex_list_nodes(Vref v)
		{
		more:
			int c = vertex_out_edge_count(v);
			if (c == 0)
				yield break;

			Eref e_rest = new Eref(-1);
			bool f_emitted = false;
			for (Eref e = vertex_first_edge(v), i_last = e + c; e < i_last; e++)
			{
				var i_feat = edge_value(e);
				if (i_feat == im.f_ix_list_head)
				{
					yield return edge_target(e);
					f_emitted = true;
				}
				else if (i_feat == im.f_ix_list_tail)
				{
					e_rest = e;
					if (f_emitted)
						break;
				}
			}
			if (f_emitted && e_rest != -1)
			{
				v = edge_target(e_rest);
				goto more;
			}
		}

		public Vref[] vertex_list_nodes(Vref v, out Eref e_rest)
		{
			Vref[] ret = NoVrefs;
			e_rest = new Eref(-1);
		more:
			int c = vertex_out_edge_count(v);
			if (c == 0)
				return ret;

			var er = new Eref(-1);
			bool f_emitted = false;
			for (Eref e = vertex_first_edge(v), i_last = e + c; e < i_last; e++)
			{
				var i_feat = edge_value(e);
				if (i_feat == im.f_ix_list_head)
				{
					arr.Append(ref ret, edge_target(e));
					f_emitted = true;
				}
				else if (i_feat == im.f_ix_list_tail)
				{
					e_rest = er = e;
					if (f_emitted)
						break;
				}
			}
			if (f_emitted && er != -1)
			{
				v = edge_target(er);
				goto more;
			}
			return ret;
		}

		public struct list_info
		{
			public Eref first;
			public Eref rest;

			public bool IsValid { get { return first >= 0 && rest >= 0; } }
		};

		public IEnumerable<list_info> vertex_list_infos(Vref v)
		{
			int c;
		more:
			if ((c = vertex_out_edge_count(v)) > 0)
			{
				list_info inf = new list_info
				{
					first = Eref.NotValid,
					rest = Eref.NotValid
				};

				for (Eref e = vertex_first_edge(v), e_last = e + c; e < e_last; e++)
				{
					var i_feat = edge_value(e);
					if (i_feat == im.f_ix_list_head)
						inf.first = e;
					else if (i_feat == im.f_ix_list_tail)
						inf.rest = e;
					else
						continue;

					if (inf.IsValid)
					{
						yield return inf;
						v = edge_target(inf.rest);
						goto more;
					}
				}
			}
		}

		static ctor_args build_ztfs(Tfs tfs, agree.Edge e)
		{
			ctor_args args;
			if (tfs is EmptyFeatureStructureTfs)
			{
				(args.V = new Vertex[1])[0] = new Vertex
				{
					e_in = Eref.None,
					e_out = Eref.NotValid,
					c_out = 0,
					value = (int)(e.FlagsId & agree.Edge.Flag.IdResolve)
				};
				args.E = NoEdges;
			}
			else
			{
				new from_array_tfs_builder(tfs, e).go(tfs, e, out args);
			}
			return args;
		}

		public unsafe struct from_array_tfs_builder
		{
			public from_array_tfs_builder(Tfs tfs, agree.Edge e)
			{
#if DEBUG
				Grammar.DebugGrammar = tfs.Grammar;
#endif
				this.rgate = tfs.entries;
				int i, c = tfs.c_act;
				this.offs = tfs.c_corefs;
				this.IMR = new int[offs + c + 2][];		// should actually be: offs+rgate.Max(ate=>ate.mark)+1
				for (i = 0; i < c; i++)
					if ((rgate[i].e_FlagsId & agree.Edge.Flag.IdResolve) != 0)
						arr.Append(ref IMR[rgate[i].mark + offs], i);

				m_v = default(Vref);
				m_e = default(Eref);
				E = null;
				V = null;
				corefs = null;
			}

			public void go(Tfs tfs, agree.Edge e, out ctor_args Result)
			{
				int* CC = stackalloc int[tfs.c_corefs];
				Edge* EE = stackalloc Edge[tfs.c_act];
				Vertex* VV = stackalloc Vertex[tfs.c_act + 1];	// add one for topmost (worst case shows up with no coreferencing)

				this.corefs = CC;
				this.E = EE;
				this.V = VV;

				Result.E = null;
				Result.V = null;

				build_node(Eref.None, e);

				int c;
				Result.E = new Edge[m_e];
				for (c = 0; c < m_e; c++)
					Result.E[c] = *E++;

				Result.V = new Vertex[m_v];
				for (c = 0; c < m_v; c++)
					Result.V[c] = *V++;
			}

			arr_tfs_entry[] rgate;
			int offs;
			int[][] IMR;

			Vref m_v;
			Eref m_e;
			Edge* E;
			Vertex* V;
			int* corefs;

			Vref build_node(Eref e, agree.Edge e_src)
			{
				Vref v = m_v;
				int m;

				if ((m = e_src.Mark) < 0)
				{
					Debug.Assert(v != 0);

					int er_coref;
					if ((er_coref = corefs[~m]) != 0)
					{
						E[e].e_next = E[--er_coref].e_next;
						E[er_coref].e_next = e;
						return E[er_coref].v_to;
					}
					corefs[~m] = e + 1;
				}

				m_v++;

				V[v] = new Vertex
				{
					e_in = e,
					value = (int)(e_src.FlagsId & agree.Edge.Flag.IdResolve)
				};

				build_arcs(v, ref V[v], m);

				return v;
			}

			void build_arcs(Vref v_par, ref Vertex par, int in_mark)
			{
				int c;
				int[] fvp;

				if (in_mark == 0 || (fvp = IMR[in_mark + offs]) == null || (c = fvp.Length) == 0)
				{
					par.e_out = Eref.NotValid;
				}
				else
				{
					Eref e;
					Edge* pe = E + (e = par.e_out = m_e);
					m_e += par.c_out = c;

					for (int ix0, q = 0; q < c; q++, e++, pe++)
					{
						ix0 = fvp[q];
						pe->v_from = v_par;
						pe->value = rgate[ix0].i_feat;
						pe->e_next = e;
						// following can change e_next, so don't re-order or use C# { } initializer syntax
						pe->v_to = build_node(e, rgate[ix0].e);
					}
				}
			}
		};


#if DEBUG
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		eent_dbg[] edbg;
		//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		[DebuggerDisplay("Count: {EdgeCount}", Name = "=== EDGES")]
		public eent_dbg[] z01_EDGES
		{
			[DebuggerStepThrough]
			get
			{
				int c = EdgeAlloc;
				if (edbg == null || edbg.Length != c)
				{
					edbg = new eent_dbg[c];
					for (Eref i = Eref.Zero; i < c; i++)
						edbg[i] = new eent_dbg(this, i);
				}
				return edbg;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		vent_dbg[] vdbg;
		//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		[DebuggerDisplay("Count: {VertexCount}", Name = "=== VERTICIES")]
		public vent_dbg[] z02_VERTICIES
		{
			[DebuggerStepThrough]
			get
			{
				int c = VertexAlloc;
				if (vdbg == null || vdbg.Length != c)
				{
					vdbg = new vent_dbg[c];
					for (Vref i = Vref.Zero; i < c; i++)
						vdbg[i] = new vent_dbg(this, i);
				}
				return vdbg;
			}
		}
#endif
	};
}
